/*
 *  Copyright (C) 2009  Peter Kist & Jan Ripke
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __world_entity_h
#define __world_entity_h 1

#include <functional>
#include <string>
#include <set>

#include <boost/unordered_map.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/weak_ptr.hpp>

#include <world/Behavior.h>
#include <world/Types.h>

namespace galaxy {
    namespace world {

        // forward decls
        class Property;

        /** All things in the game are represented by an Entity object.
        * Entities have properties and behaviors. The properties store information about 
        * the entity and the behavior objects control its behavior (based on the content of
        * the properties)
        * \par
        * The properties are stored in a hashtable of shared pointers. The behavior objects
        * refer to these properties with a weak pointer (taken from one of the shared pointers
        * in the property hash table)
        */
        class Entity : public boost::enable_shared_from_this<Entity>
        {

        public:

            typedef boost::unordered_map<std::string, Behavior*>   BehaviorMap;   /**< behavior map */
            typedef boost::unordered_map<std::string, PropertyPtr> PropertyMap; /**< property map */
            typedef std::string const                              Type;

            /** compare function object for EntitySet */
            struct EntityCompare {
                bool operator()(const EntityPtr& a, const EntityPtr& b) const {
                    std::string const & astr = a->getName();
                    std::string const & bstr = b->getName ();
                    return (astr.compare(bstr) < 0 ? true : false);
                }
            };
            /** shorthand for the set of owned entities */
            typedef std::set<EntityPtr,EntityCompare> EntitySet;

        private:

            BehaviorMap m_Behaviors;		/**< the behavior hash table */
            PropertyMap m_Properties;		/**< the propery hash table */

            /** internally kept \b seqno in generated name */
            static int m_NameGenCount;

        protected:

            std::string   m_Name;           /**< name for the entity */

            EntityWeakPtr m_Owner;            /**< owner of this entity */
            EntitySet     m_Owns;             /**< set of entities that are owned by this one */

            EntityWeakPtr m_Inside;         /**< entity in which 'this' is located */
            EntitySet     m_Contains;       /**< entities that are contained within 'this' */

            /** default constructor intentionally protected */
            Entity(void) { };			

        public:
            Entity(EntityPtr const& owner);   /**< constructs entity with specified owner */
            Entity(EntityPtr const& owner, std::string name);  /**< constructs entity with specified name and owner */
            virtual ~Entity(void);		/**< destructor */

            /** @name Entity Type Related Member Functions */
            //@{

            /** Get the entity's type */
            virtual Type& getEntityType () = 0;

            //@}

            /** @name Behavior Related Member Functions */
            //@{

            /** add a new behavior object to the entity.
            * The behavior is only added if it was not already present.
            * @param name the name of the behavior to add (registered in BehaviorFactory)
            * @param initialState the initial state for the new behavior (Behavior::ACTIVE or Behavior::INACTIVE)
            * @post the state of the object after creation will either be \b initialState 
            * or CREATED if the initializing the behavior failed.
            */
            bool addBehavior (std::string& name, Behavior::Status initialState = Behavior::INACTIVE);
            Behavior * findBehavior (std::string& name);

            //@}

            /** @name Property Related Member Functions */
            //@{

            /** find the specified property or create one if it does not already exist.
            * Any initialization for the property is the responsibility of the caller.
            * @param name The name for the property.
            * @return found or created property upon success or null upon error.
            */
            PropertyPtr obtainProperty (std::string& name);

            /** return the specified property
            * @param name The name of the required property.
            * @return an iterator that points to the found propery or points to property.end() if it was not found.
            */
            PropertyMap::iterator findProperty (std::string& name);

            /** remove the specified property
            * @param name The name of the property to remove 
            * @return true upon success, false upon failure or property not found 
            */
            bool removeProperty (std::string& name);

            //@}


            /** @name Named Entity Related Member Functions */
            //@{

            /** get name of entity. The name can only be set using the Entity's constructor and once set 
            * it cannot be changed.
            * @return the name of the entity
            */
            std::string const & getName () { return m_Name; };

            /** generate a name for this entity. This method will only return the generated name and will
            * not set the member called \b name.
            * The format of the generated name is "gn:<client-id>:<seqno>" where client id identifies the
            * current game client's id and seqno is a sequentially generated number that starts with 1 
            * and increments by 1 for every call to this method.
            * @param result the generated name
            * @return true upon success, false upon error
            */
            void getGeneratedName(std::string& result);

            //@}

            /** @name Ownership Related Member Functions */
            //@{

            /** get the owner of the entity. 
            * @param[out] result will hold the entities owner upon return
            */
            virtual void getOwner (EntityPtr& result) { result = m_Owner.lock(); };
            /** checks whether the specified entity is the owner of \b this
            * @param entity The entity to check for ownership of \b this
            * @return true if the specified entity is the owner of false if it is not
            */
            virtual bool isOwner (EntityPtr& entity) const;

            /** assign specified owner to this, removing it from its present owner
            * @param newOwner the new owner for \b this
            * @return true if ownership of \b this could be changed from the current to the new owner
            * and false upon error.
            */
            virtual bool changeOwner (EntityPtr newOwner);

            /** find named owned entity, returning the owner that was found in param \b owned
            * @param name the name of the entity to be found (as child of \b this)
            * @param result will contain the owner entity with the specified name (if found).
            * @return true when owned (named) entity was found or false if not.
            */
            bool findOwned (std::string& name, EntityPtr& result); 

            /** Get the set of owned entities. Note that this is a direct reference to the internal set
            so do not change any members in that set.
            */
            EntitySet const & getOwnedEntities () { return m_Owns; };

            //@}
        };

    }
}


#endif // __world_entity_h //
